package com.example.codediff.analyzer;

import com.example.codediff.model.CallNode;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.Map;

/**
 * 调用图构建访问器
 * 用于分析类文件中的方法调用关系
 */
public class CallGraphVisitor extends ClassVisitor {
    private String className;
    private final Map<String, CallNode> callGraph;

    public CallGraphVisitor(Map<String, CallNode> callGraph) {
        super(Opcodes.ASM9);
        this.callGraph = callGraph;
    }

    @Override
    public void visit(int version, int access, String name, String signature,
                     String superName, String[] interfaces) {
        this.className = name.replace('/', '.');
        super.visit(version, access, name, signature, superName, interfaces);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor,
                                   String signature, String[] exceptions) {
        // 创建当前方法的CallNode
        CallNode currentNode = getOrCreateNode(className, name, descriptor);
        
        return new MethodVisitor(Opcodes.ASM9, 
                super.visitMethod(access, name, descriptor, signature, exceptions)) {
            @Override
            public void visitMethodInsn(int opcode, String owner, String name,
                                      String descriptor, boolean isInterface) {
                // 记录方法调用
                String calledClass = owner.replace('/', '.');
                CallNode calledNode = getOrCreateNode(calledClass, name, descriptor);
                
                // 建立调用关系
                calledNode.getCallers().add(currentNode);
                
                super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
            }
        };
    }

    private CallNode getOrCreateNode(String className, String methodName, String descriptor) {
        String fullSignature = className + "." + methodName + descriptor;
        return callGraph.computeIfAbsent(fullSignature, k -> {
            CallNode node = new CallNode();
            node.setClassName(className);
            node.setMethodName(methodName);
            node.setMethodDesc(descriptor);
            return node;
        });
    }
}
